/**
  ******************************************************************************
  * @file    rtc.cpp
  * @author  shentq
  * @version V1.2
  * @date    2016/08/14
  * @brief
  ******************************************************************************
  * @attention
  *
  * No part of this software may be used for any commercial activities by any form
  * or means, without the prior written consent of shentq. This specification is
  * preliminary and is subject to change at any time without notice. shentq assumes
  * no responsibility for any errors contained herein.
  * <h2><center>&copy; Copyright 2015 shentq. All Rights Reserved.</center></h2>
  ******************************************************************************
  */


/* Includes ------------------------------------------------------------------*/
#include "ebox_rtc.h"
#define RTC_CFG_FLAG 0XA5A5
callback_fun_type rtc_callback_table[3];//

int Rtc::begin(uint8_t clock_source)
{

    int ret = EOK;
    /* Enable PWR and BKP clocks */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    /* Allow access to BKP Domain */
    PWR_BackupAccessCmd(ENABLE);
    /* Reset Backup Domain */
    //    BKP_DeInit();


    if(is_config(RTC_CFG_FLAG) == 0)
    {
        if(config(clock_source) != EOK)
        {
            config(0);
            ret = EPARA;
        }

        set_config_flag(RTC_CFG_FLAG);
    }
    else
    {
        /* Check if the Power On Reset flag is set */
        if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
        {
            //			uart1.printf("\r\n\n Power On Reset occurred....");
        }
        /* Check if the Pin Reset flag is set */
        else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
        {
            //			uart1.printf("\r\n\n External Reset occurred....");
        }

        //		uart1.printf("\r\n No need to configure RTC....");
        //		uart1.printf("\r\n step 0....");
        /* Wait for RTC registers synchronization */
        RTC_WaitForSynchro();
        //		uart1.printf("\r\n step 1....");

        /* Enable the RTC Second */
        RTC_ITConfig(RTC_IT_SEC, ENABLE);
        //		uart1.printf("\r\n step 2....");

        /* Wait until last write operation on RTC registers has finished */
        RTC_WaitForLastTask();

        //		uart1.printf("\r\n step 3....");
    }
    return ret;
}
int Rtc::config(uint8_t flag)
{

    int ret;
    uint32_t i = 0;
    /* Enable PWR and BKP clocks */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    /* Allow access to BKP Domain */
    PWR_BackupAccessCmd(ENABLE);

    /* Reset Backup Domain */
    BKP_DeInit();

    if(flag == 1)
    {
        /* Enable LSE */
        RCC_LSEConfig(RCC_LSE_ON);
        /* Wait till LSE is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
        {
            i++;
            if(i >= 0x1fffff)
            {
                ret = ETIMEOUT;
                return ret;
            }
        }
        /* Select LSE as RTC Clock Source */
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    }
    else
    {
        RCC_LSICmd(ENABLE);
        /* Wait till LSI is ready */
        while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);

        /* Enable RTC Clock */
        RCC_RTCCLKCmd(ENABLE);

    }

    /* Wait for RTC registers synchronization */
    RTC_WaitForSynchro();

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();

    /* Enable the RTC Second */
    RTC_ITConfig(RTC_IT_SEC, ENABLE);

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    if(flag == 1)
    {
        /* Set RTC prescaler: set RTC period to 1sec */
        RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
    }
    else
    {
        /* Set RTC prescaler: set RTC period to 1sec */
        RTC_SetPrescaler(39999); /* RTC period = RTCCLK/RTC_PR = (40 KHz)/(39999+1) */

    }

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();


    nvic(ENABLE);

    return EOK;
}
uint8_t Rtc::is_config(uint16_t configFlag)
{
    return (BKP_ReadBackupRegister(BKP_DR1) == configFlag);
}
void Rtc::set_config_flag(uint16_t configFlag)
{
    BKP_WriteBackupRegister(BKP_DR1, configFlag);
}
void Rtc::set_counter(uint32_t count)
{
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    /* Change the current time */
    RTC_SetCounter(count);

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
}
void Rtc::set_alarm(uint32_t count)
{
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    /* Change the current time */
    RTC_SetAlarm(count);

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
}


void Rtc::nvic(FunctionalState state)
{

    NVIC_InitTypeDef NVIC_InitStructure;

    /* Configure one bit for preemption priority */
    //	NVIC_PriorityGroupConfig(NVIC_GROUP_CONFIG);

    /* Enable the RTC Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = state;
    NVIC_Init(&NVIC_InitStructure);
}

void Rtc::attach_sec_interrupt(void (*cb_fun)(void))
{
    rtc_callback_table[0] = cb_fun;
}
void Rtc::attach_alarm_interrupt(void (*cb_fun)(void))
{
    rtc_callback_table[1] = cb_fun;
}
void Rtc::attach_overflow_interrupt(void (*cb_fun)(void))
{
    rtc_callback_table[2] = cb_fun;
}

void Rtc::sec_interrupt(FunctionalState state)
{
    /* Wait for RTC registers synchronization */
    RTC_WaitForSynchro();
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    if(state == ENABLE)
        RTC->CRH |= (1 << 0);
    else
        RTC->CRH &= ~(1 << 0);
}

void Rtc::alarm_interrupt(FunctionalState state)
{
    /* Wait for RTC registers synchronization */
    RTC_WaitForSynchro();
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    if(state == ENABLE)
        RTC->CRH |= (1 << 1);
    else
        RTC->CRH &= ~(1 << 1);
}

void Rtc::overflow_interrupt(FunctionalState state)
{

    /* Wait for RTC registers synchronization */
    RTC_WaitForSynchro();
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    if(state == ENABLE)
        RTC->CRH |= (1 << 2);
    else
        RTC->CRH &= ~(1 << 2);
}

uint32_t Rtc::get_counter()
{
    return RTC_GetCounter();
};
extern "C" {
    void RTC_IRQHandler(void)
    {
        if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
        {
            rtc_callback_table[0]();
            RTC_ClearITPendingBit(RTC_IT_SEC);
        }
        if (RTC_GetITStatus(RTC_IT_ALR) != RESET)
        {
            rtc_callback_table[1]();
            RTC_ClearITPendingBit(RTC_IT_ALR);
        }
        if (RTC_GetITStatus(RTC_IT_OW) != RESET)
        {
            rtc_callback_table[2]();
            RTC_ClearITPendingBit(RTC_IT_OW);
        }
    }
}
